#include "gtkpathbar.h"
#include "gtkprivate.h"
#include "gtkradiobutton.h"
+#include "gtkrecentfilter.h"
+#include "gtkrecentmanager.h"
#include "gtkscrolledwindow.h"
#include "gtkseparatormenuitem.h"
#include "gtksizegroup.h"
LOCATION_TOGGLE_POPUP,
SHOW_HIDDEN,
SEARCH_SHORTCUT,
+ RECENT_SHORTCUT,
LAST_SIGNAL
};
SHORTCUT_TYPE_PATH,
SHORTCUT_TYPE_VOLUME,
SHORTCUT_TYPE_SEPARATOR,
- SHORTCUT_TYPE_SEARCH
+ SHORTCUT_TYPE_SEARCH,
+ SHORTCUT_TYPE_RECENT
} ShortcutType;
/* Column numbers for the file list */
SEARCH_MODEL_COL_NUM_COLUMNS
};
+enum {
+ RECENT_MODEL_COL_PATH,
+ RECENT_MODEL_COL_DISPLAY_NAME,
+ RECENT_MODEL_COL_INFO,
+ RECENT_MODEL_COL_IS_FOLDER,
+ RECENT_MODEL_COL_HANDLE,
+ RECENT_MODEL_COL_NUM_COLUMNS
+};
+
/* Identifiers for target types */
enum {
GTK_TREE_MODEL_ROW,
typedef enum {
SHORTCUTS_SEARCH,
SHORTCUTS_SEARCH_SEPARATOR,
+ SHORTCUTS_RECENT,
+ SHORTCUTS_RECENT_SEPARATOR,
SHORTCUTS_HOME,
SHORTCUTS_DESKTOP,
SHORTCUTS_VOLUMES,
gint bookmark_index);
static void show_hidden_handler (GtkFileChooserDefault *impl);
static void search_shortcut_handler (GtkFileChooserDefault *impl);
+static void recent_shortcut_handler (GtkFileChooserDefault *impl);
static void update_appearance (GtkFileChooserDefault *impl);
static void set_current_filter (GtkFileChooserDefault *impl,
static void search_entry_activate_cb (GtkEntry *entry,
gpointer data);
+static void recent_manager_update (GtkFileChooserDefault *impl);
+static void recent_stop_loading (GtkFileChooserDefault *impl);
+static void recent_clear_model (GtkFileChooserDefault *impl,
+ gboolean remove_from_treeview);
+static gboolean recent_should_respond (GtkFileChooserDefault *impl);
+static void recent_switch_to_browse_mode (GtkFileChooserDefault *impl);
+static GSList * recent_get_selected_paths (GtkFileChooserDefault *impl);
+
\f
/* Drag and drop interface declarations */
NULL, NULL,
_gtk_marshal_VOID__VOID,
G_TYPE_NONE, 0);
+ signals[RECENT_SHORTCUT] =
+ _gtk_binding_signal_new ("recent-shortcut",
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (recent_shortcut_handler),
+ NULL, NULL,
+ _gtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
binding_set = gtk_binding_set_by_class (class);
GDK_s, GDK_MOD1_MASK,
"search-shortcut",
0);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_r, GDK_MOD1_MASK,
+ "recent-shortcut",
+ 0);
for (i = 0; i < 10; i++)
gtk_binding_entry_add_signal (binding_set,
if (impl->sort_model)
g_object_unref (impl->sort_model);
- search_clear_model (impl, TRUE);
+ search_clear_model (impl, FALSE);
+ recent_clear_model (impl, FALSE);
g_free (impl->preview_display_name);
return gtk_widget_render_icon (GTK_WIDGET (impl), GTK_STOCK_FIND, GTK_ICON_SIZE_MENU, NULL);
}
+static GdkPixbuf *
+render_recent_icon (GtkFileChooserDefault *impl)
+{
+ return gtk_widget_render_icon (GTK_WIDGET (impl), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL);
+}
+
/* Re-reads all the icons for the shortcuts, used when the theme changes */
struct ReloadIconsData
else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
{
pixbuf = render_search_icon (impl);
- }
+ }
+ else if (shortcut_type == SHORTCUT_TYPE_RECENT)
+ {
+ pixbuf = render_recent_icon (impl);
+ }
}
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
impl->has_search = TRUE;
}
+static void
+shortcuts_append_recent (GtkFileChooserDefault *impl)
+{
+ GdkPixbuf *pixbuf;
+ GtkTreeIter iter;
+
+ pixbuf = render_recent_icon (impl);
+
+ gtk_list_store_append (impl->shortcuts_model, &iter);
+ gtk_list_store_set (impl->shortcuts_model, &iter,
+ SHORTCUTS_COL_PIXBUF, pixbuf,
+ SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
+ SHORTCUTS_COL_NAME, _("Recently Used"),
+ SHORTCUTS_COL_DATA, NULL,
+ SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_RECENT,
+ SHORTCUTS_COL_REMOVABLE, FALSE,
+ -1);
+
+ if (pixbuf)
+ g_object_unref (pixbuf);
+
+ impl->has_recent = TRUE;
+}
+
/* Appends an item for the user's home directory to the shortcuts model */
static void
shortcuts_append_home (GtkFileChooserDefault *impl)
n += impl->has_search ? 1 : 0;
+ if (where == SHORTCUTS_RECENT)
+ goto out;
+
+ n += impl->has_recent ? 1 : 0;
+
+ if (where == SHORTCUTS_RECENT_SEPARATOR)
+ goto out;
+
+ n += impl->has_recent ? 1 : 0;
+
if (where == SHORTCUTS_HOME)
goto out;
GtkTreeIter iter;
g_assert (where == SHORTCUTS_SEARCH_SEPARATOR ||
- where == SHORTCUTS_BOOKMARKS_SEPARATOR ||
+ where == SHORTCUTS_RECENT_SEPARATOR ||
+ where == SHORTCUTS_BOOKMARKS_SEPARATOR ||
where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
gtk_list_store_insert (impl->shortcuts_model, &iter,
shortcuts_append_search (impl);
shortcuts_insert_separator (impl, SHORTCUTS_SEARCH_SEPARATOR);
}
+
+ if (impl->recent_manager)
+ {
+ shortcuts_append_recent (impl);
+ shortcuts_insert_separator (impl, SHORTCUTS_RECENT_SEPARATOR);
+ }
if (impl->file_system)
{
gboolean active;
gchar *tip;
- if (impl->operation_mode == OPERATION_MODE_SEARCH)
+ /* FIXME - Find a way to enable bookmarking items returned by
+ * a search query or inside the recently used files list
+ */
+ if (impl->operation_mode == OPERATION_MODE_SEARCH ||
+ impl->operation_mode == OPERATION_MODE_RECENT)
{
gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, FALSE);
{
file_list_build_popup_menu (impl);
- /* FMQ: handle OPERATION_MODE_SEARCH */
+ /* FIXME - handle OPERATION_MODE_SEARCH and OPERATION_MODE_RECENT */
/* The sensitivity of the Add to Bookmarks item is set in
* bookmarks_check_add_sensitivity()
name_id = SEARCH_MODEL_COL_PATH;
mtime_id = SEARCH_MODEL_COL_STAT;
break;
+ case OPERATION_MODE_RECENT:
+ name_id = RECENT_MODEL_COL_PATH;
+ mtime_id = RECENT_MODEL_COL_INFO;
+ break;
}
gtk_tree_view_column_set_sort_column_id (impl->list_name_column, name_id);
retval = FALSE;
}
}
+
+ if (impl->has_recent)
+ {
+ idx = shortcuts_get_index (impl, SHORTCUTS_RECENT);
+ if (idx == indices[0])
+ retval = FALSE;
+ else
+ {
+ idx = shortcuts_get_index (impl, SHORTCUTS_RECENT_SEPARATOR);
+ if (idx == indices[0])
+ retval = FALSE;
+ }
+ }
gtk_tree_path_free (tree_path);
static void
location_switch_to_filename_entry (GtkFileChooserDefault *impl)
{
+ /* when in search or recent files mode, we are not showing the
+ * location_entry_box container, so there's no point in switching
+ * to it.
+ */
+ if (impl->operation_mode == OPERATION_MODE_SEARCH ||
+ impl->operation_mode == OPERATION_MODE_RECENT)
+ return;
+
if (impl->location_entry)
gtk_widget_destroy (impl->location_entry);
gtk_widget_push_composite_child ();
- /* Shortcuts model */
+ /* Recent files manager */
+ recent_manager_update (impl);
+ /* Shortcuts model */
shortcuts_model_create (impl);
/* The browse widgets */
}
search_stop_searching (impl, TRUE);
+ recent_stop_loading (impl);
remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
profile_end ("end", NULL);
}
+static void
+recent_manager_update (GtkFileChooserDefault *impl)
+{
+ GtkRecentManager *manager;
+
+ profile_start ("start", NULL);
+
+ if (gtk_widget_has_screen (GTK_WIDGET (impl)))
+ manager = gtk_recent_manager_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
+ else
+ manager = gtk_recent_manager_get_default ();
+
+ if (impl->recent_manager != manager)
+ impl->recent_manager = manager;
+
+ profile_end ("end", NULL);
+}
+
static void
gtk_file_chooser_default_style_set (GtkWidget *widget,
GtkStyle *previous_style)
remove_settings_signal (impl, previous_screen);
check_icon_theme (impl);
+ recent_manager_update (impl);
g_signal_emit_by_name (widget, "default-size-changed");
struct update_chooser_entry_selected_foreach_closure closure;
const char *file_part;
- if (impl->operation_mode == OPERATION_MODE_SEARCH || !impl->location_entry)
+ if (impl->operation_mode == OPERATION_MODE_SEARCH ||
+ impl->operation_mode == OPERATION_MODE_RECENT ||
+ !impl->location_entry)
return;
if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
impl->browse_files_last_selected_name);
}
- else
+ else if (impl->operation_mode == OPERATION_MODE_SEARCH)
{
gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model),
&closure.first_selected_iter,
SEARCH_MODEL_COL_DISPLAY_NAME, &file_part,
-1);
}
+ else if (impl->operation_mode == OPERATION_MODE_RECENT)
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model),
+ &closure.first_selected_iter,
+ RECENT_MODEL_COL_DISPLAY_NAME, &file_part,
+ -1);
+ }
}
else
{
profile_start ("start", (char *) path);
- search_switch_to_browse_mode (impl);
+ switch (impl->operation_mode)
+ {
+ case OPERATION_MODE_SEARCH:
+ search_switch_to_browse_mode (impl);
+ break;
+ case OPERATION_MODE_RECENT:
+ recent_switch_to_browse_mode (impl);
+ break;
+ case OPERATION_MODE_BROWSE:
+ break;
+ }
g_assert (path != NULL);
{
GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
- if (impl->operation_mode == OPERATION_MODE_SEARCH)
+ if (impl->operation_mode == OPERATION_MODE_SEARCH ||
+ impl->operation_mode == OPERATION_MODE_RECENT)
return NULL;
if (impl->reload_state == RELOAD_EMPTY)
if (!parent_path)
return _gtk_file_chooser_set_current_folder_path (chooser, path, error);
- if (impl->operation_mode == OPERATION_MODE_SEARCH || impl->load_state == LOAD_EMPTY)
- same_path = FALSE;
+ if (impl->operation_mode == OPERATION_MODE_SEARCH ||
+ impl->operation_mode == OPERATION_MODE_RECENT ||
+ impl->load_state == LOAD_EMPTY)
+ {
+ same_path = FALSE;
+ }
else
{
g_assert (impl->current_folder != NULL);
{
GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
- if (impl->operation_mode == OPERATION_MODE_SEARCH)
+ if (impl->operation_mode == OPERATION_MODE_SEARCH ||
+ impl->operation_mode == OPERATION_MODE_RECENT)
{
GtkTreeSelection *selection;
if (impl->operation_mode == OPERATION_MODE_SEARCH)
return search_get_selected_paths (impl);
+ if (impl->operation_mode == OPERATION_MODE_RECENT)
+ return recent_get_selected_paths (impl);
+
info.impl = impl;
info.result = NULL;
info.path_from_entry = NULL;
goto file_entry;
else
{
- /* The focus is on a dialog's action area button or something else */
- if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ /* The focus is on a dialog's action area button or something else */
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
goto file_entry;
else
goto file_list;
if (impl->operation_mode == OPERATION_MODE_SEARCH)
return search_should_respond (impl);
+ if (impl->operation_mode == OPERATION_MODE_RECENT)
+ return recent_should_respond (impl);
+
selection_check (impl, &num_selected, &all_files, &all_folders);
if (num_selected > 2)
error = NULL;
if (is_folder)
{
- if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
- || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
{
change_folder_and_display_error (impl, path, TRUE);
retval = FALSE;
}
- else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
- || GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
{
/* The folder already exists, so we do not need to create it.
* Just respond to terminate the dialog.
impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
- if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
- || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
{
if (impl->location_mode == LOCATION_MODE_PATH_BAR)
widget = impl->browse_files_tree_view;
else
widget = impl->location_entry;
}
- else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
widget = impl->location_entry;
else
{
static void
search_switch_to_browse_mode (GtkFileChooserDefault *impl)
{
- if (impl->operation_mode == OPERATION_MODE_BROWSE)
- return;
+ g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
search_stop_searching (impl, FALSE);
search_clear_model (impl, TRUE);
static void
search_activate (GtkFileChooserDefault *impl)
{
+ OperationMode previous_mode;
+
if (impl->operation_mode == OPERATION_MODE_SEARCH)
{
gtk_widget_grab_focus (impl->search_entry);
return;
}
+ previous_mode = impl->operation_mode;
impl->operation_mode = OPERATION_MODE_SEARCH;
+ switch (previous_mode)
+ {
+ case OPERATION_MODE_RECENT:
+ recent_stop_loading (impl);
+ recent_clear_model (impl, TRUE);
+ break;
+
+ case OPERATION_MODE_BROWSE:
+ stop_loading_and_clear_list_model (impl);
+ break;
+
+ case OPERATION_MODE_SEARCH:
+ g_assert_not_reached ();
+ break;
+ }
+
g_assert (impl->search_hbox == NULL);
g_assert (impl->search_entry == NULL);
g_assert (impl->search_model == NULL);
- stop_loading_and_clear_list_model (impl);
search_setup_widgets (impl);
file_list_set_sort_column_ids (impl);
}
+/*
+ * Recent files support
+ */
+
+/* Frees the data in the recent_model */
+static void
+recent_clear_model (GtkFileChooserDefault *impl,
+ gboolean remove_from_treeview)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (!impl->recent_model)
+ return;
+
+ model = GTK_TREE_MODEL (impl->recent_model);
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ do
+ {
+ GtkFilePath *file_path;
+ GtkFileSystemHandle *handle;
+ GtkRecentInfo *recent_info;
+ gchar *display_name;
+
+ gtk_tree_model_get (model, &iter,
+ RECENT_MODEL_COL_DISPLAY_NAME, &display_name,
+ RECENT_MODEL_COL_PATH, &file_path,
+ RECENT_MODEL_COL_HANDLE, &handle,
+ RECENT_MODEL_COL_INFO, &recent_info,
+ -1);
+
+ if (handle)
+ gtk_file_system_cancel_operation (handle);
+
+ gtk_file_path_free (file_path);
+ gtk_recent_info_unref (recent_info);
+ g_free (display_name);
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+ }
+
+ g_object_unref (impl->recent_model);
+ impl->recent_model = NULL;
+
+ if (remove_from_treeview)
+ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
+}
+
+/* Stops any ongoing loading of the recent files list; does
+ * not touch the recent_model
+ */
+static void
+recent_stop_loading (GtkFileChooserDefault *impl)
+{
+ if (impl->load_recent_id)
+ {
+ g_source_remove (impl->load_recent_id);
+ impl->load_recent_id = 0;
+ }
+}
+
+/* Stops any pending load, clears the file list, and switches
+ * back to OPERATION_MODE_BROWSE
+ */
+static void
+recent_switch_to_browse_mode (GtkFileChooserDefault *impl)
+{
+ g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
+
+ recent_stop_loading (impl);
+ recent_clear_model (impl, TRUE);
+
+ gtk_widget_show (impl->browse_path_bar);
+ gtk_widget_show (impl->browse_new_folder_button);
+
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ {
+ gtk_widget_show (impl->location_button);
+
+ if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
+ gtk_widget_show (impl->location_entry_box);
+ }
+
+ impl->operation_mode = OPERATION_MODE_BROWSE;
+
+ file_list_set_sort_column_ids (impl);
+}
+
+/* Sort callback from the modification time column */
+static gint
+recent_column_mtime_sort_func (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ GtkRecentInfo *info_a, *info_b;
+
+ gtk_tree_model_get (model, a, RECENT_MODEL_COL_INFO, &info_a, -1);
+ gtk_tree_model_get (model, b, RECENT_MODEL_COL_INFO, &info_b, -1);
+
+ if (gtk_recent_info_get_modified (info_a) < gtk_recent_info_get_modified (info_b))
+ return -1;
+ else if (gtk_recent_info_get_modified (info_a) > gtk_recent_info_get_modified (info_b))
+ return 1;
+ else
+ return 0;
+}
+
+
+static void
+recent_setup_model (GtkFileChooserDefault *impl)
+{
+ g_assert (impl->recent_model == NULL);
+
+ /* We store these columns in the search model:
+ *
+ * RECENT_MODEL_COL_PATH - a pointer to GtkFilePath for the hit's URI,
+ * stored as a pointer and not as a GTK_TYPE_FILE_PATH;
+ * RECENT_MODEL_COL_DISPLAY_NAME - a string with the display name,
+ * stored as a pointer and not as a G_TYPE_STRING;
+ * RECENT_MODEL_COL_INFO - GtkRecentInfo, stored as a pointer and not
+ * as a GTK_TYPE_RECENT_INFO;
+ * RECENT_MODEL_COL_IS_FOLDER - boolean flag;
+ * RECENT_MODEL_COL_HANDLE - GtkFileSystemHandle, stored as a pointer
+ * and not as a GTK_TYPE_FILE_SYSTEM_HANDLE;
+ *
+ * Keep this in sync with the enumeration defined near the beginning of
+ * this file.
+ */
+ impl->recent_model = gtk_list_store_new (RECENT_MODEL_COL_NUM_COLUMNS,
+ G_TYPE_POINTER,
+ G_TYPE_POINTER,
+ G_TYPE_POINTER,
+ G_TYPE_BOOLEAN,
+ G_TYPE_POINTER);
+
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
+ RECENT_MODEL_COL_INFO,
+ recent_column_mtime_sort_func,
+ impl,
+ NULL);
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->recent_model),
+ RECENT_MODEL_COL_INFO,
+ GTK_SORT_DESCENDING);
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
+ GTK_TREE_MODEL (impl->recent_model));
+}
+
+typedef struct
+{
+ GtkFileChooserDefault *impl;
+ GList *items;
+ gint n_items;
+ gint n_loaded_items;
+} RecentLoadData;
+
+static void
+recent_idle_cleanup (gpointer data)
+{
+ RecentLoadData *load_data = data;
+
+ set_busy_cursor (load_data->impl, FALSE);
+
+ load_data->impl->load_recent_id = 0;
+
+ if (load_data->items)
+ {
+ g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL);
+ g_list_free (load_data->items);
+ }
+
+ g_free (load_data);
+}
+
+struct RecentItemInsertRequest
+{
+ GtkFileChooserDefault *impl;
+ GtkFilePath *path;
+ GtkTreeRowReference *row_ref;
+};
+
+static void
+recent_item_get_info_cb (GtkFileSystemHandle *handle,
+ const GtkFileInfo *info,
+ const GError *error,
+ gpointer data)
+{
+ gboolean cancelled = handle->cancelled;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GtkFileSystemHandle *model_handle;
+ gboolean is_folder = FALSE;
+ struct RecentItemInsertRequest *request = data;
+
+ path = gtk_tree_row_reference_get_path (request->row_ref);
+ if (!path)
+ goto out;
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->recent_model),
+ &iter, path);
+ gtk_tree_path_free (path);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (request->impl->recent_model), &iter,
+ RECENT_MODEL_COL_HANDLE, &model_handle,
+ -1);
+ if (handle != model_handle)
+ goto out;
+
+ gtk_list_store_set (request->impl->recent_model, &iter,
+ RECENT_MODEL_COL_HANDLE, NULL,
+ -1);
+
+ if (cancelled)
+ goto out;
+
+ if (!info)
+ {
+ gtk_list_store_remove (request->impl->recent_model, &iter);
+ goto out;
+ }
+
+ is_folder = gtk_file_info_get_is_folder (info);
+
+ gtk_list_store_set (request->impl->recent_model, &iter,
+ RECENT_MODEL_COL_IS_FOLDER, is_folder,
+ -1);
+
+out:
+ g_object_unref (request->impl);
+ gtk_file_path_free (request->path);
+ gtk_tree_row_reference_free (request->row_ref);
+ g_free (request);
+
+ g_object_unref (handle);
+}
+
+static gboolean
+recent_idle_load (gpointer data)
+{
+ RecentLoadData *load_data = data;
+ GtkFileChooserDefault *impl = load_data->impl;
+ GtkTreeIter iter;
+ GtkTreePath *p;
+ GtkRecentInfo *info;
+ const gchar *uri, *display_name;
+ GtkFilePath *path;
+ GtkFileSystemHandle *handle;
+ struct RecentItemInsertRequest *request;
+
+ if (!impl->recent_manager)
+ return FALSE;
+
+ if (!load_data->items)
+ {
+ load_data->items = gtk_recent_manager_get_items (impl->recent_manager);
+ load_data->n_items = g_list_length (load_data->items);
+ load_data->n_loaded_items = 0;
+
+ return TRUE;
+ }
+
+ info = g_list_nth_data (load_data->items, load_data->n_loaded_items);
+ g_assert (info != NULL);
+
+ uri = gtk_recent_info_get_uri (info);
+ display_name = gtk_recent_info_get_display_name (info);
+ path = gtk_file_system_uri_to_path (impl->file_system, uri);
+ if (!path)
+ goto load_next;
+
+ gtk_list_store_append (impl->recent_model, &iter);
+ p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->recent_model), &iter);
+
+ request = g_new0 (struct RecentItemInsertRequest, 1);
+ request->impl = g_object_ref (impl);
+ request->path = gtk_file_path_copy (path);
+ request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->recent_model), p);
+ gtk_tree_path_free (p);
+
+ handle = gtk_file_system_get_info (impl->file_system, path,
+ GTK_FILE_INFO_IS_FOLDER,
+ recent_item_get_info_cb,
+ request);
+
+ gtk_list_store_set (impl->recent_model, &iter,
+ RECENT_MODEL_COL_PATH, path,
+ RECENT_MODEL_COL_DISPLAY_NAME, g_strdup (display_name),
+ RECENT_MODEL_COL_INFO, gtk_recent_info_ref (info),
+ RECENT_MODEL_COL_HANDLE, handle,
+ -1);
+
+load_next:
+
+ load_data->n_loaded_items += 1;
+
+ /* finished loading items */
+ if (load_data->n_loaded_items == load_data->n_items)
+ {
+ g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL);
+ g_list_free (load_data->items);
+ load_data->items = NULL;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+recent_start_loading (GtkFileChooserDefault *impl)
+{
+ RecentLoadData *load_data;
+
+ recent_stop_loading (impl);
+ recent_clear_model (impl, TRUE);
+ recent_setup_model (impl);
+ set_busy_cursor (impl, TRUE);
+
+ if (!impl->recent_manager)
+ recent_manager_update (impl);
+
+ g_assert (impl->load_recent_id == 0);
+
+ load_data = g_new (RecentLoadData, 1);
+ load_data->impl = impl;
+ load_data->items = NULL;
+ load_data->n_items = 0;
+ load_data->n_loaded_items = 0;
+
+ /* begin lazy loading the recent files into the model */
+ impl->load_recent_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30,
+ recent_idle_load,
+ load_data,
+ recent_idle_cleanup);
+}
+
+static void
+recent_selected_foreach_get_path_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ GSList **list;
+ const GtkFilePath *file_path;
+ GtkFilePath *file_path_copy;
+
+ list = data;
+
+ gtk_tree_model_get (model, iter, RECENT_MODEL_COL_PATH, &file_path, -1);
+ file_path_copy = gtk_file_path_copy (file_path);
+ *list = g_slist_prepend (*list, file_path_copy);
+}
+
+/* Constructs a list of the selected paths in recent files mode */
+static GSList *
+recent_get_selected_paths (GtkFileChooserDefault *impl)
+{
+ GSList *result;
+ GtkTreeSelection *selection;
+
+ result = NULL;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
+ gtk_tree_selection_selected_foreach (selection, recent_selected_foreach_get_path_cb, &result);
+ result = g_slist_reverse (result);
+
+ return result;
+}
+
+/* Called from ::should_respond(). We return whether there are selected
+ * files in the recent files list.
+ */
+static gboolean
+recent_should_respond (GtkFileChooserDefault *impl)
+{
+ GtkTreeSelection *selection;
+
+ g_assert (impl->operation_mode == OPERATION_MODE_RECENT);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
+ return (gtk_tree_selection_count_selected_rows (selection) != 0);
+}
+
+/* Hide the location widgets temporarily */
+static void
+recent_hide_entry (GtkFileChooserDefault *impl)
+{
+ gtk_widget_hide (impl->browse_path_bar);
+ gtk_widget_hide (impl->browse_new_folder_button);
+
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ {
+ gtk_widget_hide (impl->location_button);
+ gtk_widget_hide (impl->location_entry_box);
+ }
+
+ /* EB: hide the filter combo? */
+}
+
+/* Main entry point to the recent files functions; this gets called when
+ * the user activates the Recently Used shortcut.
+ */
+static void
+recent_activate (GtkFileChooserDefault *impl)
+{
+ OperationMode previous_mode;
+
+ if (impl->operation_mode == OPERATION_MODE_RECENT)
+ return;
+
+ previous_mode = impl->operation_mode;
+ impl->operation_mode = OPERATION_MODE_RECENT;
+
+ switch (previous_mode)
+ {
+ case OPERATION_MODE_SEARCH:
+ search_stop_searching (impl, FALSE);
+ search_clear_model (impl, TRUE);
+
+ gtk_widget_destroy (impl->search_hbox);
+ impl->search_hbox = NULL;
+ impl->search_entry = NULL;
+ break;
+
+ case OPERATION_MODE_BROWSE:
+ stop_loading_and_clear_list_model (impl);
+ break;
+
+ case OPERATION_MODE_RECENT:
+ g_assert_not_reached ();
+ break;
+ }
+
+ recent_hide_entry (impl);
+ file_list_set_sort_column_ids (impl);
+ recent_start_loading (impl);
+}
+
+
static void
set_current_filter (GtkFileChooserDefault *impl,
GtkFileFilter *filter)
new_display_name = gtk_file_info_get_display_name (new_info);
}
}
- else
+ else if (impl->operation_mode == OPERATION_MODE_SEARCH)
{
GtkTreeIter iter;
SEARCH_MODEL_COL_DISPLAY_NAME, &new_display_name,
-1);
}
+ else if (impl->operation_mode == OPERATION_MODE_RECENT)
+ {
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model), &iter, cursor_path);
+ gtk_tree_path_free (cursor_path);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &iter,
+ RECENT_MODEL_COL_PATH, &new_path,
+ RECENT_MODEL_COL_DISPLAY_NAME, &new_display_name,
+ -1);
+ }
}
if (new_path != impl->preview_path &&
{
GtkFilePath *path;
- search_switch_to_browse_mode (impl);
+ switch (impl->operation_mode)
+ {
+ case OPERATION_MODE_BROWSE:
+ break;
+ case OPERATION_MODE_SEARCH:
+ search_switch_to_browse_mode (impl);
+ break;
+ case OPERATION_MODE_RECENT:
+ recent_switch_to_browse_mode (impl);
+ break;
+ }
/* We ref the file chooser since volume_mount() may run a main loop, and the
* user could close the file chooser window in the meantime.
{
search_activate (impl);
}
+ else if (shortcut_type == SHORTCUT_TYPE_RECENT)
+ {
+ recent_activate (impl);
+ }
}
/* Callback used when a row in the shortcuts list is activated */
GtkFileChooserDefault *impl)
{
/* See if we are in the new folder editable row for Save mode */
- if (impl->operation_mode == OPERATION_MODE_BROWSE && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+ if (impl->operation_mode == OPERATION_MODE_BROWSE &&
+ impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
{
const GtkFileInfo *info;
gboolean had_selection;
g_signal_emit_by_name (impl, "file-activated");
}
break;
+
+ case OPERATION_MODE_RECENT:
+ {
+ GtkFilePath *file_path;
+ gboolean is_folder;
+
+ if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model), &iter, path))
+ return;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &iter,
+ RECENT_MODEL_COL_PATH, &file_path,
+ RECENT_MODEL_COL_IS_FOLDER, &is_folder,
+ -1);
+
+ if (is_folder)
+ {
+ change_folder_and_display_error (impl, file_path, FALSE);
+ recent_switch_to_browse_mode (impl);
+ return;
+ }
+
+ g_signal_emit_by_name (impl, "file-activated");
+ }
+ break;
case OPERATION_MODE_BROWSE:
{
-1);
sensitive = TRUE;
break;
+
+ case OPERATION_MODE_RECENT:
+ {
+ GtkRecentInfo *info;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), iter,
+ RECENT_MODEL_COL_INFO, &info,
+ -1);
+
+ pixbuf = gtk_recent_info_get_icon (info, impl->icon_size);
+
+ if (impl->local_only && !gtk_recent_info_is_local (info))
+ sensitive = FALSE;
+ }
+ break;
case OPERATION_MODE_BROWSE:
{
return;
}
+ if (impl->operation_mode == OPERATION_MODE_RECENT)
+ {
+ char *display_name;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), iter,
+ RECENT_MODEL_COL_DISPLAY_NAME, &display_name,
+ -1);
+ g_object_set (cell,
+ "text", display_name,
+ "sensitive", TRUE,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ NULL);
+ return;
+ }
+
info = get_list_file_info (impl, iter);
sensitive = TRUE;
}
- if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
- || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
{
sensitive = gtk_file_info_get_is_folder (info);
}
-1);
time_mtime = statbuf->st_mtime;
}
+ else if (impl->operation_mode == OPERATION_MODE_RECENT)
+ {
+ GtkRecentInfo *info;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), iter,
+ RECENT_MODEL_COL_INFO, &info,
+ -1);
+ time_mtime = (GtkFileTime) gtk_recent_info_get_modified (info);
+
+ if (impl->local_only && !gtk_recent_info_is_local (info))
+ sensitive = FALSE;
+ }
else
{
const GtkFileInfo *info;
location_popup_handler (GtkFileChooserDefault *impl,
const gchar *path)
{
- if (impl->operation_mode == OPERATION_MODE_SEARCH)
+ if (impl->operation_mode != OPERATION_MODE_BROWSE)
{
GtkWidget *widget_to_focus;
/* This will give us the location widgets back */
- search_switch_to_browse_mode (impl);
+ switch (impl->operation_mode)
+ {
+ case OPERATION_MODE_SEARCH:
+ search_switch_to_browse_mode (impl);
+ break;
+ case OPERATION_MODE_RECENT:
+ recent_switch_to_browse_mode (impl);
+ break;
+ case OPERATION_MODE_BROWSE:
+ g_assert_not_reached ();
+ break;
+ }
+
if (impl->current_folder)
change_folder_and_display_error (impl, impl->current_folder, FALSE);
gtk_widget_grab_focus (widget_to_focus);
return;
}
- if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
- || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
{
LocationMode new_mode;
}
}
}
- else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
+ impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
{
gtk_widget_grab_focus (impl->location_entry);
if (path != NULL)
switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_SEARCH));
}
+/* Handler for the "recent-shortcut" keybinding signal */
+static void
+recent_shortcut_handler (GtkFileChooserDefault *impl)
+{
+ if (impl->has_recent)
+ switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_RECENT));
+}
+
static void
quick_bookmark_handler (GtkFileChooserDefault *impl,
gint bookmark_index)